//methods for testing BitVector32
#include "testBitVector32.h"
#include <iostream>

//main test method
bool test_BV32(){

  //start to test constructor
  bool status=test_BV32_construct();
  if(!status){
    std::cout << "Failed: Testing constructor\n";
    return false;
  }
  std::cout << "Passed: test_BV32_construct()\n";
  
  //test toString
  status=test_BV32_toString();
  if(!status){
    std::cout << "Failed: Testing toString\n";
    return false;
  }
  std::cout << "Passed: test_BV32_toString()\n";

 //test bit manipulation
  status=test_BV32_bit();
  if(!status){
    std::cout << "Failed: Bit manipulation\n";
    return false;
  }
  std::cout << "Passed: test_BV32_bit()\n";
  
  //test logic operations
  status=test_BV32_logic();
  if(!status){
    std::cout << "Failed: Logic operations\n";
    return false;
  }
  std::cout << "Passed: test_BV32_logic()\n";

  //test append
  status=test_BV32_append();
  if(!status){
    std::cout << "Failed: Append\n";
    return false;
  }
  std::cout << "Passed: test_BV32_append()\n";

  //test weight

  //return true when all test pass
  status=test_BV32_weight();
  if(!status){
    std::cout << "Failed: Weight\n";
    return false;
  }
  std::cout << "Passed: test_BV32_weight()\n";
  return true;
}

//testing constructor methods
bool test_BV32_construct(){
  //test empty constructor
  std::string refString="";
  BitVector32 bv1;
  std::string testString=bv1.toBinStringLSB();
  if(testString!=refString){
    std::cout << "Failed empty constructor\n";
    return false;
  }
  
  //test constuctor with predefined size
  BitVector32 bv2(13);
  refString="0000000000000";
  testString=bv2.toBinStringLSB();
  if(testString!=refString){
    std::cout << "Failed constructor length 13\n";
    return false;
  }
  BitVector32 bv3(43);
  refString="0000000000000000000000000000000000000000000";
  testString=bv3.toBinStringLSB();
  if(testString!=refString){
    std::cout << "Failed constructor length 43\n";
    return false;
  }
  
  //test constuctor that takes a string as input
  refString="10011010101111001101";
  BitVector32 bv4(refString);
  testString=bv4.toBinStringLSB();
  if(testString!=refString){
    std::cout << "Failed constructor short string\n";
    return false;
  }
  refString="000100100011010001010110011110001001101010111100110111101111010";
  BitVector32 bv5(refString);
  testString=bv5.toBinStringLSB();
  if(testString!=refString){
    std::cout << "Failed constructor length 43\n";
  return false;
  }
  return true;
}


//testing toString methods
bool test_BV32_toString(){
  //test Binary LSB
  std::string refString="101011";
  BitVector32 bv1(refString);
  std::string testString=bv1.toBinStringLSB();
  if(testString!=refString){
    std::cout << "Failed toBinLSB length 6\n";
    return false;
  }
  
  //test Binary MSB
  refString="110101";
  testString=bv1.toBinStringMSB();
  if(testString!=refString){
    std::cout << "Failed toBinMSB length 6\n";
    return false;
  }
  
  //test Hex MSB
  refString="35";
  testString=bv1.toHexStringMSB();
  if(testString!=refString){
    std::cout << "Failed toHexMSB length 6\n";
    return false;
  }
  
  //test Hex LSB
  refString="AC";
  testString=bv1.toHexStringLSB();
  if(testString!=refString){
    std::cout << "Failed toHexLSB length 6\n";
    return false;
  }
  
  //rerun the test with a vector that contains exactly 64 bits
  refString="1111111011011100101110101001100000000001001000110100010101100111";
  //test binary LSB
  BitVector32 bv2(refString);
  testString=bv2.toBinStringLSB();
  if(testString!=refString){
    std::cout << "Failed toBinLSB length 64\n";
    return false;
  }
  //test binary MSB
  refString="1110011010100010110001001000000000011001010111010011101101111111";
  testString=bv2.toBinStringMSB();
  if(testString!=refString){
    std::cout << "Failed toBinMSB length 64\n";
    return false;
  }
  //testHexLSB
  refString="FEDCBA98 01234567";
  testString=bv2.toHexStringLSB();
  if(testString!=refString){
    std::cout << "Failed toHexLSB length 64\n";
    std::cout << testString << std::endl;
    return false;
  }  
  //testHexMSB
  refString="E6A2C480 195D3B7F";
  testString=bv2.toHexStringMSB();
  if(testString!=refString){
    std::cout << "Failed toHexLSB length 64\n";
    std::cout << testString << std::endl;
    return false;
  }  

  //rerun the test with a vector that contains 65 bits
  refString="11111110110111001011101010011000000000010010001101000101011001111";
  //test binary LSB
  BitVector32 bv3(refString);
  testString=bv3.toBinStringLSB();
  if(testString!=refString){
    std::cout << "Failed toBinLSB length 65\n";
    return false;
  }
  //test binary MSB
  refString="11110011010100010110001001000000000011001010111010011101101111111";
  testString=bv3.toBinStringMSB();
  if(testString!=refString){
    std::cout << "Failed toBinMSB length 65\n";
    return false;
  }
  //testHexLSB
  refString="FEDCBA98 01234567 8";
  testString=bv3.toHexStringLSB();
  if(testString!=refString){
    std::cout << "Failed toHexLSB length 65\n";
    std::cout << testString << std::endl;
    return false;
  }  
  //testHexMSB
  refString="1 E6A2C480 195D3B7F";
  testString=bv3.toHexStringMSB();
  if(testString!=refString){
    std::cout << "Failed toHexLSB length 65\n";
    std::cout << testString << std::endl;
    return false;
  }
  
  //all test pass
  return true;
}

//testing get, set, clear and setVal
bool test_BV32_bit(){
  //start to test set
  BitVector32 bv(100);
  for(int i=0;i<100;i+=2)
    bv.setBit(i);
  std::string refString="AAAAAAAA AAAAAAAA AAAAAAAA A";
  std::string testString=bv.toHexStringLSB();
  if(testString!=refString){
    std::cout << "Failed setBit\n";
    std::cout << testString << std::endl;
    return false;
  }
  for(int i=1;i<100;i+=2)
    bv.setBit(i);
  refString="FFFFFFFF FFFFFFFF FFFFFFFF F";
  testString=bv.toHexStringLSB();
  if(testString!=refString){
    std::cout << "Failed setBit\n";
    std::cout << testString << std::endl;
    return false;
  }
  //test clear
  for(int i=1;i<100;i+=2)
    bv.clearBit(i);
  refString="AAAAAAAA AAAAAAAA AAAAAAAA A";
  testString=bv.toHexStringLSB();
  if(testString!=refString){
    std::cout << "Failed clearBit\n";
    std::cout << testString << std::endl;
    return false;
  }
  for(int i=0;i<100;i++)
    bv.clearBit(i);
  refString="00000000 00000000 00000000 0";
  testString=bv.toHexStringLSB();
   if(testString!=refString){
    std::cout << "Failed clearBit\n";
    std::cout << testString << std::endl;
    return false;
  }
  //test setVal
  for(int i=0;i<100;i++)
    bv.setVal(i,i&1);
  refString="55555555 55555555 55555555 5";
  testString=bv.toHexStringLSB();
  if(testString!=refString){
    std::cout << "Failed setVal\n";
    std::cout << testString << std::endl;
    return false;
  }
  // test getBit
  int N=10000;
  BitVector32 bv2(N);
  BitVector32 bv3(N);
  //set bits in bv2
  for(int i=0;i<N;i+=3)
    bv2.setBit(i);
  for(int i=0;i<N;i+=7)
    bv2.setBit(i);
  for(int i=0;i<N;i+=11)
    bv2.setBit(i);
  for(int i=0;i<N;i+=2)
    bv2.clearBit(i);
  for(int i=0;i<N;i+=5)
    bv2.setBit(i);
  //copy bv2 to bv3 using get and setVal
  for(int i=0;i<N;i++)
    bv3.setVal(i,bv2.getBit(i));
  refString=bv2.toHexStringMSB();
  testString=bv3.toHexStringMSB();
  if(testString!=refString){
    std::cout << "Failed getBit\n";
    std::cout << testString << std::endl;
    return false;
  }
  return true;
}

//testing logical operators
bool test_BV32_logic(){
  BitVector32 bv1(100);
  BitVector32 bv2(100);
  for(int i=0;i<100;i+=2)
    bv1.setBit(i);
  //make bv2 equal to bv1 by or
  bv2.orOp(bv1);
  std::string refString=bv1.toHexStringMSB();
  std::string testString=bv2.toHexStringMSB();
  if(testString!=refString){
    std::cout << testString << std::endl;
    std::cout << "Failed: orOp\n";
    return false;
  }
  //set all bits to 1 in bv2
  for(int i=0;i<100;i++)
    bv2.setBit(i);
  //use and to get back to bv1
  bv2.andOp(bv1);
  testString=bv2.toHexStringMSB();
  if(testString!=refString){
    std::cout << testString << std::endl;
    std::cout << "Failed: andOp\n";
    return false;
  }
  //set all bits to zero with xor
  bv2.xorOp(bv1);
  testString=bv2.toHexStringMSB();
  refString="0 00000000 00000000 00000000";
  if(testString!=refString){
    std::cout << testString << std::endl;
    std::cout << "Failed: xorOp\n";
    return false;
  }
  //set odd bits to 1 in bv2
  for(int i=1;i<100;i+=2)
    bv2.setBit(i);
  bv2.notOp();
  testString=bv2.toHexStringMSB();
  refString=bv1.toHexStringMSB();
  if(testString!=refString){
    std::cout << testString << std::endl;
    std::cout << "Failed: notOp\n";
    return false;
  }
  //all tests pass
  return true;
}


//test append
bool test_BV32_append(){
  //first test append bit
  BitVector32 bv1("00010010001101000101011001111000100110101011110011011110111111011");
  BitVector32 bv2;
  for(unsigned i=0;i<bv1.length();i++)
    bv2.appendBit(bv1.getBit(i));
  if((bv1.toHexStringMSB()!=bv2.toHexStringMSB())||(bv1.length()!=bv2.length())){
    std::cout << bv1.length() << "\t" << bv2.length()<< std::endl;
    std::cout << bv1.toHexStringMSB() << std::endl;
    std::cout << bv2.toHexStringMSB() << std::endl;
    return false;
  }
  //append 32-bit word, everything aligned
  BitVector32 bv3;
  word32 w(0x12345678uL);
  bv3.appendWord32(w);
  w.assign(0xABBACDCDuL);
  bv3.appendWord32(w);
  w.assign(0xEFEF00EFuL);
  bv3.appendWord32(w);
  std::string refString="EFEF00EF ABBACDCD 12345678";
  std::string testString=bv3.toHexStringMSB();
  if(testString!=refString){
    std::cout << testString << std::endl;
    std::cout << bv3.length() << std::endl;
    std::cout << "Failed: appendWord32\n";
    return false;
  }
  //append 32-bit word when not aligned in bitVector
  BitVector32 bv4;
  bv4.appendBit(1);
  bv4.appendBit(0);
  bv4.appendBit(1);
  bv4.appendBit(1);
  bv4.appendWord32(w);
  testString= bv4.toHexStringMSB();
  refString="E FEF00EFD";
  if(testString!=refString){
    std::cout << testString << std::endl;
    std::cout << bv4.length() << std::endl;
    std::cout << "Failed: appendWord32\n";
    return false;
  }
 
  //run another test when only 1 bit in vector
  BitVector32 bv5;
  bv5.appendBit(1);
  bv5.appendWord32(w);
  testString= bv5.toHexStringMSB();
  refString="1 DFDE01DF";
  if(testString!=refString){
    std::cout << testString << std::endl;
    std::cout << bv5.length() << std::endl;
    std::cout << "Failed: appendWord32\n";
    return false;
  }
  //run another test when 31 bit in vector
  BitVector32 bv6;
  for(int i=0;i<31;i++)
    bv6.appendBit(i&1);
  bv6.appendWord32(w);
  testString = bv6.toHexStringMSB();
  refString="77F78077 AAAAAAAA";
  if(testString!=refString){
    std::cout << testString << std::endl;
    std::cout << bv6.length() << std::endl;
    std::cout << "Failed: appendWord32\n";
    return false;
  }

  //all test pass
  return true;
}

//test weight
bool test_BV32_weight(){
  BitVector32 bv;
  //create a bit vector with 100 bits
  bv.appendWord32(word32(0x12341234uL)); //weight 10
  bv.appendWord32(word32(0xcdefcdefuL)); //weight 24
  bv.appendWord32(word32(0xaa55a5a5uL)); //weight 16
  bv.appendBit(1);
  bv.appendBit(0);
  bv.appendBit(0);
  bv.appendBit(1);
  unsigned w=bv.weight();
  //if correct w=52
  if(w!=52){
    std::cout << "Fail weight\n";
    return false;
  }

  //all test pass
  return true;
}
